/**
 * 
 */
package net.toocruel.iqismart.entity.rbac;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.jsonwebtoken.lang.Collections;
import net.toocruel.iqismart.entity.base.BaseEntity;
import net.toocruel.iqismart.entity.constants.NotificationType;
import net.toocruel.iqismart.entity.constants.UserStatus;
import net.toocruel.iqismart.entity.core.*;
import net.toocruel.iqismart.entity.user.UserData;
import net.toocruel.iqismart.entity.user.UserDetail;
import net.toocruel.iqismart.entity.user.UserOauth;
import net.toocruel.iqismart.entity.validate.group.ControllerGroup;
import net.toocruel.iqismart.entity.validate.group.RegisterGroup;
import net.toocruel.iqismart.utils.FileUploadUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import springfox.documentation.annotations.ApiIgnore;

import javax.persistence.*;
import javax.validation.constraints.Pattern;
import java.util.*;
import java.util.Collection;
import java.util.function.Consumer;

/**
 * 用户
 *
 * @author toocruel
 */
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "username"),
		@UniqueConstraint(columnNames = "email"),
		@UniqueConstraint(columnNames = "mobile")})
public class User extends BaseEntity implements UserDetails,Cloneable {
	/**
	 * 用户名
	 */

	@NotBlank(message = "用户名不能为空",groups = RegisterGroup.class)
	@Length(min = 4,max = 20,message = "长度为5-20",groups = RegisterGroup.class)
	private String username;
	private String nickname;
	/**
	 * 密码
	 * 强：字母+数字+特殊字符
	 *  ^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&*]+$)(?![\d!@#$%^&*]+$)[a-zA-Z\d!@#$%^&*]+$
	 *
	 *
	 * 中：字母+数字，字母+特殊字符，数字+特殊字符
	 *      ^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&*]+$)[a-zA-Z\d!@#$%^&*]+$
	 *
	 * 弱：纯数字，纯字母，纯特殊字符
	 * ^(?:\d+|[a-zA-Z]+|[!@#$%^&*]+)$
	 */
	@Pattern(regexp = "^(?![a-zA-z]+$)(?!\\d+$)(?![!@#$%^&*]+$)[a-zA-Z\\d!@#$%^&*]+$",message = "字母+数字/字母+特殊字符/数字+特殊字符",groups = RegisterGroup.class)
	@Length(min = 6,max = 20,groups = RegisterGroup.class)
	private String password;
	private Long passwordLastModify;
	@Transient
	private String passwordConfirmation;

	private String avatar;
	@Transient
	private String avatarFull;
	@Email
	private String email;
	private String mobile;

	@OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	@JoinColumn(name = "userDetailId")
	private UserDetail detail = new UserDetail();

	@OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	@JoinColumn(name = "userDataId")
	private UserData data = new UserData();

	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	private Set<UserOauth> oauths = new HashSet<>();

	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,mappedBy = "user")
	private Set<Question> questions = new HashSet<>();

	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,mappedBy = "user")
	private Set<Answer> answers = new HashSet<>();

	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,mappedBy = "user")
	private Set<Article> articles = new HashSet<>();


	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,mappedBy = "user")
	private Set<Comment> comments = new HashSet<>();

	/**
	 * 赞我的
	 */
	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,mappedBy = "toUser")
	private Set<Support> supports = new HashSet<>();

	@Enumerated(EnumType.ORDINAL)
	private UserStatus status = UserStatus.REVIEW_PENDING;

	@ElementCollection
	@Column(name = "notificationTypeId")
	@JoinTable(name = "user_site_notification",joinColumns = @JoinColumn(name = "userId"))
	private Set<NotificationType> siteNotifications = NotificationType.defaultSiteNotifications();

	@ElementCollection
	@Column(name = "notificationTypeId")
	@JoinTable(name = "user_email_notification",joinColumns = @JoinColumn(name = "userId"))
	private Set<NotificationType> emailNotifications = NotificationType.defaultEmailNotifications();

	@OneToOne
	@JoinColumn(name = "authenticationId")
	private Authentication authentication;

	/**
	 * 用户的所有角色
	 */
	@ManyToMany(cascade = CascadeType.DETACH)
	@JoinTable(name = "user_role_relation",joinColumns = @JoinColumn(name = "userId"),inverseJoinColumns = @JoinColumn(name = "roleId"))
	@JsonIgnoreProperties("users")
	private Set<Role> roles = new HashSet<>();
	/**
	 * 角色id
	 */
	@NotEmpty(message = "角色id不能为空",groups = ControllerGroup.class)
	@Transient
	private Long[] roleIds;

	/**
	 * 用户有权访问的所有url，不持久化到数据库
	 */
	@Transient
	private Set<String> urls = new HashSet<>();
	/**
	 * 用户有权的所有资源id，不持久化到数据库
	 */
	@Transient
	private Set<Long> resourceIds = new HashSet<>();

	@Transient
	private Boolean admin = false;

	public Long getPasswordLastModify() {
		return passwordLastModify;
	}

	public void setPasswordLastModify(Long passwordLastModify) {
		this.passwordLastModify = passwordLastModify;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.core.userdetails.UserDetails#getAuthorities(
	 * )
	 */
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList("ROLE_USER");
		roles.forEach(role -> {
			if(role.getId() == 2){
				authorityList.addAll(AuthorityUtils.createAuthorityList("ROLE_ADMIN"));
			}
		});
		return authorityList;
	}

	/**
	 * Gets all resource ids.
	 *
	 * @return the all resource ids
	 */
	public Set<Long> getAllResourceIds() {
		//init
		init(resourceIds);
		forEachResource(resource -> resourceIds.add(resource.getId()));
		return resourceIds;
	}

	/**
	 * Gets urls.
	 *
	 * @return the urls
	 */
	public Set<String> getUrls() {
		init(urls);
		forEachResource(resource -> urls.addAll(resource.getUrls()));
		return urls;
	}

	/**
	 * @param data
	 */
	private void init(Set<?> data){
		if (CollectionUtils.isEmpty(data)) {
			if (data == null) {
				data = new HashSet<>();
			}
		}
	}

	/**
	 * @param consumer
	 */
	private void forEachResource(Consumer<Resource> consumer) {
		for (Role role : roles) {
			for (Resource resource : role.getResources()) {
				consumer.accept(resource);
			}
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.core.userdetails.UserDetails#getUsername()
	 */
	public String getUsername() {
		return username;
	}

	/**
	 * Sets username.
	 *
	 * @param username the username
	 */
	public void setUsername(String username) {
		this.username = username;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.core.userdetails.UserDetails#getPassword()
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * Sets password.
	 *
	 * @param password the password
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.core.userdetails.UserDetails#
	 * isAccountNonExpired()
	 */
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.core.userdetails.UserDetails#
	 * isAccountNonLocked()
	 */
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.core.userdetails.UserDetails#
	 * isCredentialsNonExpired()
	 */
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.core.userdetails.UserDetails#isEnabled()
	 */
	@Override
	public boolean isEnabled() {
		return true;
	}

	/**
	 * Gets roles.
	 *
	 * @return the roles
	 */
	@ApiIgnore
	public Set<Role> getRoles() {
		return roles;
	}

	/**
	 * Sets roles.
	 *
	 * @param roles the roles
	 */
	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}

	/**
	 * Sets urls.
	 *
	 * @param urls the urls
	 */
	public void setUrls(Set<String> urls) {
		this.urls = urls;
	}

	/**
	 * Gets resource ids.
	 *
	 * @return the resource ids
	 */
	public Set<Long> getResourceIds() {
		return resourceIds;
	}

	/**
	 * Sets resource ids.
	 *
	 * @param resourceIds the resource ids
	 */
	public void setResourceIds(Set<Long> resourceIds) {
		this.resourceIds = resourceIds;
	}

	@Override
	public int hashCode() {
		return username.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		return this.toString().equals(obj.toString());
	}

	@Override
	public String toString() {
		return username;
	}

	/**
	 * Gets avatar.
	 *
	 * @return the avatar
	 */
	public String getAvatar() {



		if(StringUtils.isBlank(avatar) && id!=null){
			return "/image/avatar/"+id;
		}
		return avatar;
	}

	/**
	 * Sets avatar.
	 *
	 * @param avatar the avatar
	 */
	public void setAvatar(String avatar) {
		if(StringUtils.isNotBlank(avatar)){
			this.avatar = avatar;
		}
	}

	/**
	 * Gets email.
	 *
	 * @return the email
	 */
	public String getEmail() {
		return email;
	}

	/**
	 * Sets email.
	 *
	 * @param email the email
	 */
	public void setEmail(String email) {
		this.email = email;
	}

	/**
	 * Gets mobile.
	 *
	 * @return the mobile
	 */
	public String getMobile() {
		return mobile;
	}

	/**
	 * Sets mobile.
	 *
	 * @param mobile the mobile
	 */
	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	/**
	 * Gets info.
	 *
	 * @return the info
	 */
	public UserDetail getDetail() {
		return detail;
	}

	/**
	 * Sets info.
	 *
	 * @param info the info
	 */
	public void setDetail(UserDetail info) {
		this.detail = info;
	}

	/**
	 * Gets status.
	 *
	 * @return the status
	 */
	public UserStatus getStatus() {
		return status;
	}

	/**
	 * Sets status.
	 *
	 * @param status the status
	 */
	public void setStatus(UserStatus status) {
		this.status = status;
	}

	/**
	 * Gets site notifications.
	 *
	 * @return the site notifications
	 */
	public Set<NotificationType> getSiteNotifications() {
		return siteNotifications;
	}

	/**
	 * Sets site notifications.
	 *
	 * @param siteNotifications the site notifications
	 */
	public void setSiteNotifications(Set<NotificationType> siteNotifications) {
		this.siteNotifications = siteNotifications;
	}

	/**
	 * Gets email notifications.
	 *
	 * @return the email notifications
	 */
	public Set<NotificationType> getEmailNotifications() {
		return emailNotifications;
	}

	/**
	 * Sets email notifications.
	 *
	 * @param emailNotifications the email notifications
	 */
	public void setEmailNotifications(Set<NotificationType> emailNotifications) {
		this.emailNotifications = emailNotifications;
	}

	public Long[] getRoleIds() {
		if(!Collections.isEmpty(roles)){
			List<Long> ids = new ArrayList<>();
			roles.stream().forEach(role -> {
				ids.add(role.getId());
			});
			return ids.toArray(new Long[ids.size()]);

		}
		return roleIds;
	}

	public void setRoleIds(Long[] roleIds) {
		this.roleIds = roleIds;
	}

	public UserData getData() {
		return data;
	}

	public void setData(UserData data) {
		this.data = data;
	}

	public Set<UserOauth> getOauths() {
		return oauths;
	}

	public void setOauths(Set<UserOauth> oauths) {
		this.oauths = oauths;
	}

	public User() {
//		oauths.add(new UserOauth(this));
	}

	public Authentication getAuthentication() {
		return authentication;
	}

	public void setAuthentication(Authentication authentication) {
		this.authentication = authentication;
	}

	public Boolean getAdmin() {
		roles.forEach(role -> {
			if(role.getName().contains("管理员")){
				admin = true ;
			}
		});
		return admin;
	}

	public void setAdmin(Boolean admin) {
		this.admin = admin;
	}

	public Set<Question> getQuestions() {
		return questions;
	}

	public void setQuestions(Set<Question> questions) {
		this.questions = questions;
	}

	public Set<Answer> getAnswers() {
		return answers;
	}

	public void setAnswers(Set<Answer> answers) {
		this.answers = answers;
	}

	public Set<Article> getArticles() {
		return articles;
	}

	public void setArticles(Set<Article> articles) {
		this.articles = articles;
	}

	public Set<Comment> getComments() {
		return comments;
	}

	public void setComments(Set<Comment> comments) {
		this.comments = comments;
	}

	public Set<Support> getSupports() {
		return supports;
	}

	public void setSupports(Set<Support> supports) {
		this.supports = supports;
	}

	public String getPasswordConfirmation() {
		return passwordConfirmation;
	}

	public void setPasswordConfirmation(String passwordConfirmation) {
		this.passwordConfirmation = passwordConfirmation;
	}

	public String getAvatarFull() {
		if(id!=null){
			return FileUploadUtils.getFullAvatar(this.getId());
		}

		return avatarFull;
	}

	public void setAvatarFull(String avatarOrgin) {
		this.avatarFull = avatarOrgin;
	}

	public String getNickname() {
		if(StringUtils.isBlank(nickname)){
			return username;
		}
		return nickname;
	}

	public void setNickname(String nickname) {
		this.nickname = nickname;
	}
}
